//
//  DoorLockFrame.swift
//  ZhiTing
//
//  Created by iMac on 2022/5/7.
//

import Foundation


/// 帧格式
/// |头码（1byte)|同步码（1byte）|数据包长（1byte）|命令类型（1byte）|命令（1byte）|设备ID（4byte）|数据（nbyte）|校验和（1byte）|
/// 头码： 0xFF
/// 同步码： 0x55
/// 数据包长：从“命令类型”到“校验和”之间的数据字节数（不包括校验和）。
/// 命令类型：0x0A 为远程应用指令；0x0B 为近程配置指令，应用于智能锁与内置无线
/// 模块之间。
/// 命令：详见下文。
/// 设备 ID: SVN 唯一性 4 个字节序列号。
/// 数据：根据不同的命令，字节数不同，也可以没有数据。
/// 校验和 CHK：从“数据包长”到“校验和”（不包括校验和）之间的数据累加和，不
/// 计累加和的进位，再按位取反。

extension DoorLockUtil {
    // MARK: - 帧数据
    enum OperationFrame {
        /// 远程增加用户
        case addUserRemotely(data: Data?)
        /// 远程不带编号增加用户
        case addAnoymousUserRemotely(user: DoorLockPwdType)
        /// 远程删除用户
        case deleteUser(type: DoorOpenType, id: Int)
        /// 远程设置门锁
        case setLockSetting(type: DoorSettingType)
        /// 远程设置常开
        case setAlwaysOpen
        /// 远程开/关门
        case setDoorOpenState(isOpen: Bool)
        /// 远程密码验证
        case pwdValidation(data: Data?)
        /// 门锁状态查询
        case queryLockStatus(type: DoorSettingQueryType)
        /// 门锁电量查询
        case queryLockBattery
        /// 存储状态查询
        case queryStorageStatus(data: Data?)
        /// 用户已注册编号查询
        case queryRegisteredUser(type: DoorRegisteredQuertType)
        /// 设置系统时间
        case setSystemTime(date: String)
        /// 获取所有日志
        case queryLog
        
        /// 头码
        var head: UInt8 {
            return 0xFF
        }
        
        /// 同步码
        var sync: UInt8 {
            return 0x55
        }
        
        /// 数据包长 (从“命令类型”到“校验和”之间的数据字节数（不包括校验和）)
        var dataLength: UInt8 {
            return UInt8(6 + (self.data?.count ?? 0))
        }
        
        /// 命令模式(远程: 0x0A、近程: 0x0B、 日志: 0x0C)
        var cmdMode: UInt8 {
            switch self {
            case .queryLog:
                return 0x0C
            default:
                return 0x0A
            }
        }
        
        /// 命令
        var cmdType: UInt8 {
            switch self {
            case .addUserRemotely:
                return 0x10
            case .addAnoymousUserRemotely:
                return 0x15
            case .deleteUser:
                return 0x11
            case .setLockSetting:
                return 0x13
            case .setAlwaysOpen:
                return 0x14
            case .setDoorOpenState:
                return 0x20
            case .pwdValidation:
                return 0x30
            case .queryLockStatus:
                return 0x21
            case .queryLockBattery:
                return 0x22
            case .queryStorageStatus:
                return 0x27
            case .queryRegisteredUser:
                return 0x28
            case .setSystemTime:
                return 0x26
            case .queryLog:
                return 0xFF
            }
        }
        
        
        /// 数据包
        var data: Data? {
            /// 加密位
            let key: [UInt8] = [0x00, 0x00, 0x00, 0x00]
            
            switch self {
            case .addUserRemotely(let data),
                    .pwdValidation(let data),
                    .queryStorageStatus(let data):
                return data
                
            case .queryLockBattery:
                return Data(key)
                
            case .queryRegisteredUser(let type):
                let bytes = key + [type.rawValue]
                return Data(bytes)

            case .setAlwaysOpen:
                let bytes = key + [0x00, 0x00, 0x00, 0x01]
                return Data(bytes)
                
            case .queryLockStatus(let type):
                let bytes = key + [type.rawValue]
                return Data(bytes)
                
            case .setLockSetting(let type):
                let bytes: [UInt8]
                switch type {
                case .language(let lang):
                    bytes = key + [0x01, lang.rawValue]
                case .volume(let vol):
                    bytes = key + [0x03, vol.rawValue]
                }
                return Data(bytes)

            case .addAnoymousUserRemotely(let user):
                return user.data
                
            case .deleteUser(let type, let id):
                var idBytes = String(id, radix: 16, uppercase: true).hexaBytes
                if idBytes.count < 2 {
                    idBytes = [0x00] + idBytes
                }
                let bytes = key + [type.rawValue] + idBytes
                return Data(bytes)
                
            case .setDoorOpenState(let isOpen):
                let bytes = key + [0x00, 0x00, 0x00] + (isOpen ? [0x02] : [0x01])
                return Data(bytes)
                
            case .setSystemTime(let date):
                let bytes = key + date.suffix(12).hexaBytes
                return Data(bytes)
                
            case .queryLog:
                return Data([0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
            }
        }
        
        
        
        /// 生成通讯数据包
        /// - Parameter device_id: 设备id
        /// - Returns: 通讯数据包
        func frameData(device_id: [UInt8]) -> Data {
            /// 校验和CHK：从“数据包长”到“校验和”（不包括校验和）之间的数据累加和，不计累加和的进位，再按位取反。
            let sum = Int(dataLength)
            + Int(cmdMode)
            + Int(cmdType)
            + device_id.compactMap { Int($0) }.reduce(0, +)
            + (data?.compactMap { Int($0) }.reduce(0, +) ?? 0)
            
            let chk = ~UInt8(sum % 256)
            

            let data = [head, sync, dataLength, cmdMode, cmdType] + Array(device_id) + Array(data ?? Data()) + [chk]
            return Data(data)
        }
        
    }
    
    
}




/// 用户权限
/// 数据：加密位 + 用户类型 + 用户数据  + 用户权限 + 权限信息
/// 加密位：(4bytes)由中继模块填充，全部为 0 即可
/// 用户类型：（1byte） 0x01――增加密码
/// 用户数据：密码：(3bytes)密码长度固定为 6 位（比如密码 547632，依次为 0x54,0x76,0x32）
/// 用户权限： （1byte） 0x01―― 普通用户 0x04―― 胁迫用户 0x02―― 一次性密码用户 0x03―― 时段用户 0x05―― 次数限制时段用户
/// 权限信息： （11 bytes）次数限制（1 byte） + 时段信息（10 bytes）
/// 次数限制：（1 byte）最大值为 200 次，0xFF 代表没有次数限制。
/// 时段信息:（10 bytes），包含有效期和开门时段信息。格式为 16010116031309001200，为时间的 BCD格式。意义为：有效期（20）160101-（20）160313；开门时段 09:00-12:00。全部为 0xFF 代表没有时段限制。 a、普通，胁迫，一次性密码用户没有权限信息。11 bytes 默认全部为 0xFF。 b、 时段用户，次数限制默认为 0xFF。后面 10字节为时段信息。 c、 次数限制时段用户, 次数限制（1 byte） + 时段信息（10 bytes）。权限信息全部为 0xFF
/// 则默认为一次性密码用户。(一次性和时段是该指令的子集)
extension DoorLockUtil {
    enum DoorLockPwdType {
        /// 普通用户
        case normal(name: String?, pwd: String)
        /// 胁迫用户
        case threaten(name: String?, pwd: String)
        /// 一次性密码用户
        case oneTime(name: String?, pwd: String)
        /// 时段用户
        case scope(name: String?, pwd: String, valid_time: String)
        /// 次数限制时段用户
        case scopeTimeLimited(name: String?, pwd: String, timeLimit: Int, startDate: String, endDate: String,
                              startTime: String, endTime: String)
        
        
        var number_name: String? {
            switch self {
            case .normal(let name, _):
                return name
            case .threaten(let name, _):
                return name
            case .oneTime(let name, _):
                return name
            case .scope(let name, _, _):
                return name
            case .scopeTimeLimited(let name, _, _, _, _, _, _):
                return name
            }
        }
        
        var pwd: String {
            switch self {
            case .normal(_, let pwd):
                return pwd
            case .threaten(_, let pwd):
                return pwd
            case .oneTime(_, let pwd):
                return pwd
            case .scope(_, let pwd, _):
                return pwd
            case .scopeTimeLimited(_, let pwd, _, _, _, _, _):
                return pwd
            }
        }

        var valid_time: String? {
            switch self {
            case .normal, .threaten, .oneTime:
                return nil
            case .scope(_, _, let valid_time):
                return valid_time
                
            case .scopeTimeLimited(_, _, _, let startDate, let endDate, let startTime, let endTime):
                let startDateBytes = String(startDate.suffix(6))
                let endDateBytes = String(endDate.suffix(6))
                let startTimeBytes = String(startTime.suffix(4))
                let endTimeBytes = String(endTime.suffix(4))
                return startDateBytes + endDateBytes + startTimeBytes + endTimeBytes
            }
        }
        
        var role_type: Int {
            switch self {
            case .normal:
                return 1
            case .threaten:
                return 4
            case .oneTime:
                return 2
            case .scope:
                return 3
            case .scopeTimeLimited:
                return 5
            }
        }

        var data: Data {
            /// 加密位
            let key: [UInt8] = [0x00, 0x00, 0x00, 0x00]
            /// 用户类型
            let mode: [UInt8] = [0x01]

            switch self {
            case .normal(_, let pwd):
                let pwdBytes = pwd.hexaBytes
                let bytes = key + mode + pwdBytes + [0x01] + Array.init(repeating: 0xFF, count: 11)
                return Data(bytes)
            case .threaten(_, let pwd):
                let pwdBytes = pwd.hexaBytes
                let bytes = key + mode + pwdBytes + [0x04] + Array.init(repeating: 0xFF, count: 11)
                return Data(bytes)
            case .oneTime(_, let pwd):
                let pwdBytes = pwd.hexaBytes
                let part1 = key + mode + pwdBytes + [0x02] + [0x01]
                let part2 = Array<UInt8>.init(repeating: 0xFF, count: 10)
                let bytes =  part1 + part2
                return Data(bytes)
            case .scope(_, let pwd, let valid_time):
                let pwdBytes = pwd.hexaBytes
                let part1 = key + mode + pwdBytes + [0x03] + [0xFF]
                let part2 = valid_time.hexaBytes
                let bytes = part1 + part2
                return Data(bytes)
            case .scopeTimeLimited(_, let pwd, let timeLimit, let startDate, let endDate, let startTime, let endTime):
                let pwdBytes = pwd.hexaBytes
                let timeLimitByte = UInt8(timeLimit > 200 ? 200 : timeLimit)
                let startDateBytes = startDate.suffix(6).hexaBytes
                let endDateBytes = endDate.suffix(6).hexaBytes
                let startTimeBytes = startTime.suffix(4).hexaBytes
                let endTimeBytes = endTime.suffix(4).hexaBytes

                let part1 = key + mode + pwdBytes + [0x05] + [timeLimitByte]
                let part2 = startDateBytes + endDateBytes + startTimeBytes + endTimeBytes
                let bytes =  part1 + part2
                return Data(bytes)
            }
        }

    }
    
}


extension DoorLockUtil {
    /// 远程删除用户类型
    enum DoorOpenType: UInt8 {
        case pwd = 0x01
        case fingerPrint = 0x02
        case remoteControl = 0x03
        case nfc = 0x06
    }
    
    enum DoorRegisteredQuertType: UInt8 {
        /// 所有
        case all = 0x00
        /// 指纹
        case fingerPrint = 0x01
        /// 密码
        case pwd = 0x02
        /// 卡
        case nfc = 0x03
    }
    
    /// 查询门锁设置
    enum DoorSettingQueryType: UInt8 {
        case setting = 0x01
        case alarm = 0x02
        case openState = 0x05
    }

    /// 远程设置门锁类型
    enum DoorSettingType {
        /// 设置语言
        case language(Language)
        /// 设置音量
        case volume(Volume)
        
        enum Language: UInt8 {
            case chinese = 0x00
            case english = 0x01
        }
        
        enum Volume: UInt8 {
            case low = 0x03
            case medium = 0x02
            case high = 0x01
            case mute = 0x04
        }

    }
}
